package cn.zswltech.gruul.web.api.intercept;

import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.zswltech.gruul.common.annotations.Desensitization;
import cn.zswltech.gruul.common.enums.DesensitizationFieldEnum;
import org.apache.commons.collections.CollectionUtils;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @create: 2022-08-27
 **/
@Component
@Intercepts(@Signature(type = ResultSetHandler.class,
        method = "handleResultSets",
        args = {Statement.class}))
public class DesensitizationIntercept implements Interceptor {

    private Map<Class, List<Field>> fieldMap = new ConcurrentHashMap<>();

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 获取结果
        List<Object> results = (List<Object>) invocation.proceed();
        if (CollectionUtils.isEmpty(results)) {
            return results;
        }
        // 批量设置脱敏
        for (Object object : results) {
            if (object != null) {
                sensitive(object);
            }
        }
        return results;
    }

    private void sensitive(Object source) {
        // 拿到返回值类型
        Class<?> sourceClass = source.getClass();
        List<Field> fieldList = fieldMap.get(sourceClass);
        SYNC:
        if (fieldList == null) {
            synchronized (this) {
                fieldList = fieldMap.get(sourceClass);
                //如果到这里map集合已经存在，则跳出到指定SYNC标签
                if (fieldList != null) {
                    break SYNC;
                }
                //获取带有Desensitization注解的所有属性字段,包含父类
                Field[] fieldsDirectly = ReflectUtil.getFieldsDirectly(sourceClass, true);
                List<Field> fields = Stream.of(fieldsDirectly)
                        .filter(field -> field.isAnnotationPresent(Desensitization.class))
                        .collect(Collectors.toList());
                fieldMap.put(sourceClass, fieldList = new ArrayList<>(fields));
            }
        }
        // 初始化返回值类型的 MetaObject
        MetaObject metaObject = SystemMetaObject.forObject(source);
        // 捕捉到属性上的标记注解 @Desensitization 并进行对应的脱敏处理
        for (Field o : fieldList) {
            doSensitive(metaObject, o);
        }

    }

    private void doSensitive(MetaObject metaObject, Field field) {
        // 拿到属性名
        String name = field.getName();
        // 获取属性值
        Object value = metaObject.getValue(name);
        // 只有字符串类型才能脱敏  而且不能为null
        if (String.class == metaObject.getGetterType(name) && value != null) {
            Desensitization annotation = field.getAnnotation(Desensitization.class);
            // 获取对应的脱敏策略 并进行脱敏
            DesensitizationFieldEnum type = annotation.value();
            Object o = desensitization(type,value);
            // 把脱敏后的值塞回去
            metaObject.setValue(name, o);
        }
    }

    private Object desensitization(DesensitizationFieldEnum type,Object value){
        switch (type){
            case PHONE:
                return DesensitizedUtil.mobilePhone(String.valueOf(value));
            default:
                return "******";
        }
    }
}
